package se.webbzon.oschi01.world;

import java.awt.Point;
import java.awt.geom.Point2D;
import java.util.Observable;

import se.webbzon.oschi01.nbt.CompoundTag;
import se.webbzon.oschi01.nbt.CompoundTagFactory;
import se.webbzon.oschi01.nbt.IntTag;
import se.webbzon.oschi01.nbt.IntTagFactory;
import se.webbzon.oschi01.nbt.Tag;
import se.webbzon.oschi01.nbt.TagEvent;
import se.webbzon.oschi01.nbt.TagGeneration;
import se.webbzon.oschi01.nbt.TagListener;
import se.webbzon.oschi01.singleton.Singletons;

public class CoordinateMediator extends Observable {
	
	protected static final int compoundTagId = Singletons.instance(CompoundTagFactory.class).getId(); 
	
	protected TagListener coordinateListener;
	
	protected Point coordinate = new Point();
	
	protected Tag<?> root;
	
	public Tag<?> getRoot() {
		return root;
	}
	
	public void attachTo(Tag<?> root) {
		if (root == null) {
			throw new IllegalArgumentException("The provided root must not be null.");
		} else if (this.root == null) {
			coordinateListener = new TagListenerImpl();
			this.root = root;
			root.addListener(coordinateListener);
			
			if (root.hasChildren()) {
				outer: for (Tag<?> child : root.getChildren()) {
					if (child.toString().equals("Level") && child.hasChildren()) {
						// Read tags if they exist
						for (Tag<?> grandChild : child.getChildren()) {
							if (grandChild.toString().equals("xPos")) {
								Object value = grandChild.getValue();
								if (value instanceof Integer)
									coordinate.x = (Integer) value;
							} else if (grandChild.toString().equals("zPos")) {
								Object value = grandChild.getValue();
								if (value instanceof Integer)
									coordinate.y = (Integer) value;
							}
						}
						break outer;
					}
				}
			}
		} else
			throw new IllegalStateException("The CoordinateMediator is already attached to a root.");
	}
	
	public void detach() {
		if (root == null)
			throw new IllegalStateException("The CoordinateMediator is not attached to a root.");
		else {
			root.removeListener(coordinateListener);
			root = null;
			coordinateListener = null;
		}
	}

	@SuppressWarnings("unchecked")
	public void setCoordinate(int x, int z) {
		if (x != coordinate.x || z != coordinate.y) {
			coordinate.setLocation(x, z);
			if (root == null)
				return;
			if (root.getFactory().getId() != compoundTagId)
				throw new UnsupportedOperationException("The root is not a compound type.");
			
			TagGeneration<Iterable<Tag<?>>> children = (TagGeneration<Iterable<Tag<?>>>) root.getChildren();
			boolean hasLevel = false; // Whatever the root has a "Level" tag 
			
			if (root.hasChildren()) {	
				outer: for (Tag<?> child : root.getChildren()) {
					if (child.toString().equals("Level") && child.getFactory().getId() == compoundTagId) {
						hasLevel = true;
						boolean xSet = false, zSet = false;
						TagGeneration<Integer> grandChildren = (TagGeneration<Integer>) child.getChildren();
						
						// Update tags if they exist
						for (Tag<?> grandChild : grandChildren) {
							if (grandChild.getValueClass() == Integer.class) {
								Tag<Integer> iTag = (Tag<Integer>) grandChild;
								if (iTag.toString().equals("xPos")) {
									iTag.setValue(x);
									xSet = true;
								} else if (iTag.toString().equals("zPos")) {
									iTag.setValue(z);
									zSet = true;
								}
							}
						}
						// Add tags if they do not exist
						if (!xSet) {
							IntTag tag = new IntTag(Singletons.instance(IntTagFactory.class),"xPos",x);
							grandChildren.addSibling(tag);
						}
						if (!zSet) {
							IntTag tag = new IntTag(Singletons.instance(IntTagFactory.class),"zPos",z);
							grandChildren.addSibling(tag);
						}
						break outer;
					}
				}
			} 
			
			// Add level if it does not exist
			if (!hasLevel) {
				IntTag xTag = new IntTag(Singletons.instance(IntTagFactory.class),"xPos",x);
				IntTag zTag = new IntTag(Singletons.instance(IntTagFactory.class),"zPos",z);
				CompoundTag level = new CompoundTag(Singletons.instance(CompoundTagFactory.class),"Level",xTag,zTag);
				children.addSibling(level);
			}
		}
	}
	
	public Point2D getCoordinate() {
		return coordinate;
	}
	
	protected final class TagListenerImpl implements TagListener {

		@Override public final void onTagEvent(TagEvent e) {
			if (e.source().hasParent()) {
				Tag<?> parent = e.source().getParent();
				if (parent.hasParent() && !parent.getParent().hasParent() && parent.toString().equals("Level")) {
					String tagName = e.source().toString();
					if (tagName.equals("xPos")) {
						Object value = e.source().getValue();
						if (value instanceof Integer) {
							int x = (Integer) value;
							if (x != coordinate.x) {
								coordinate.x = x;
								setChanged();
								notifyObservers(coordinate);
							}
						}
					} else if (tagName.equals("zPos")) {
						Object value = e.source().getValue();
						if (value instanceof Integer) {
							int z = (Integer) value;
							if (z != coordinate.y) {
								coordinate.y = z;
								setChanged();
								notifyObservers(coordinate);
							}
						}
					}
				}
			}
		}

	}
	
}
